package com.syh.jn.scm.domain.supplier.entity.stock;

import cn.redscarf.commons.util.bean.ExtBeanUtils;
import cn.redscarf.commons.util.other.ExtAssert;
import cn.redscarf.commons.spring.SpringContextUtils;
import cn.redscarf.commons.util.validator.ValidationUtils;
import cn.redscarf.commons.wrapper.Restrictions;
import cn.redscarf.commons.wrapper.Wrapper;
import com.syh.core.ddd.domain.CodeExpEnum;
import com.syh.core.ddd.domain.CodeGenerateUtil;
import com.syh.jn.scm.domain.supplier.entity.purchase.PurchaseOrder;
import com.syh.jn.scm.domain.supplier.entity.purchase.PurchaseOrderItem;
import com.syh.jn.scm.domain.supplier.entity.purchase.PurchaseReturnOrder;
import com.syh.jn.scm.domain.supplier.entity.purchase.PurchaseReturnOrderItem;
import com.syh.jn.scm.domain.supplier.entity.sale.SaleOrder;
import com.syh.jn.scm.domain.supplier.entity.sale.SaleOrderItem;
import com.syh.jn.scm.domain.supplier.entity.sale.SaleReturnOrder;
import com.syh.jn.scm.domain.supplier.entity.sale.SaleReturnOrderItem;
import com.syh.jn.scm.domain.supplier.repository.purchase.PurReturnOrderReadRepository;
import com.syh.jn.scm.domain.supplier.repository.purchase.PurchaseOrderReadRepository;
import com.syh.jn.scm.domain.supplier.repository.sale.SaleOrderReadRepository;
import com.syh.jn.scm.domain.supplier.repository.sale.SaleReturnOrderReadRepository;
import com.syh.jn.scm.domain.support.SuperBusinessAggregate;
import com.syh.jn.scm.dto.supplier.stock.StockLossMto;
import com.syh.jn.scm.dto.supplier.stock.StockLossSto;
import com.syh.jn.scm.exception.BusinessException;
import com.syh.jn.scm.service.api.supplier.stock.enums.StockLossEnums;
import lombok.AccessLevel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * 仓库报损管理(聚合)
 */
@Data
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(callSuper = true)
public class StockLoss extends SuperBusinessAggregate<Long> {

  public StockLoss(StockLossSto sto) {
    ExtAssert.notNull(sto, "新增对象为空");
    ValidationUtils.validate(sto);
    super.initDefault();
    List<Long> goodsIds = this.selectOriginalOrder(sto.getOriginalOrderCode(), sto.getLossType());
    this.status = StockLossEnums.Status.STAY_CONFIRM.getStatus();
    this.haveRecord = StockLossEnums.HaveRecord.NOT_RECORD.getHaveRecord();
    this.lockStatus = StockLossEnums.LockStatus.UNLOCK.getLockStatus();
    this.lossOrderCode = CodeGenerateUtil.codeGenerate(CodeExpEnum.STOCK_LOSS_BILL_EXP.getCodeExp(), sto.getStorehouseId());
    ExtBeanUtils.copyProperties(sto, this);
    //明细相关
    this.lossItems = new ArrayList<>();
    sto.getItems().stream().forEach(itemSto -> {
      if (goodsIds != null && !checkGoods(itemSto.getGoodsId(),goodsIds)) {
        throw new BusinessException("原始订单不包含该商品:"+itemSto.getGoodsName());
      }
      this.addItem(new StockLossItem(itemSto));
    });
    this.calculateTotalAmount();
  }

  /**
   * 计算报损金额
   */
  private void calculateTotalAmount() {
    BigDecimal totalAmount = BigDecimal.ZERO;
    for (StockLossItem item : this.lossItems) {
      totalAmount = totalAmount.add(item.getQuantity().multiply(item.getAverageCost()));
    }
    //报损金额
    this.totalAmount = totalAmount;
  }

  /**
   * 修改报损单
   *
   * @param mto
   */
  public void modify(StockLossMto mto) {
    ExtAssert.notNull(mto, "修改对象为空");
    ValidationUtils.validate(mto);
    ExtBeanUtils.copyProperties(mto, this);
    List<Long> goodsIds = this.selectOriginalOrder(mto.getOriginalOrderCode(), mto.getLossType());
    this.lossItems = new ArrayList<>();
    //明细相关
    mto.getItems().stream().forEach(itemMto -> {
      if (goodsIds != null && !checkGoods(itemMto.getGoodsId(),goodsIds)) {
        throw new BusinessException("原始订单不包含该商品:"+itemMto.getGoodsName());
      }
      this.addItem(new StockLossItem(itemMto));
    });
    this.calculateTotalAmount();
    this.lastUpdateTimeStamp = System.currentTimeMillis();
  }

  /**
   * 查找原始订单
   * @param orderCode 单号
   * @param lossType 订单类型
   * @return 订单商品id列表
   *
   */
  private List<Long> selectOriginalOrder(String orderCode, Short lossType) {
    List<Long> goodsIds = null;
    if (orderCode != null && !"".equals(orderCode.trim())){
      orderCode = orderCode.trim();
      if (lossType == null) {
        throw new BusinessException("请选择单据类型");
      }
      this.lossType = lossType;
      switch (lossType) {
        //查找销售单
        case 1:
          SaleOrderReadRepository saleOrderReadRepository = (SaleOrderReadRepository) SpringContextUtils.getBean("saleOrderRepositoryImpl");
          Wrapper saleWrapper = Wrapper.build().append(Restrictions.eq("code",orderCode));
          SaleOrder saleOrder = saleOrderReadRepository.getOne(saleWrapper);
          if (saleOrder == null) {
            throw new BusinessException("找不到单号为"+orderCode+"的销售单，请检查单号或单据类型");
          }
          saleOrder = saleOrderReadRepository.getById(saleOrder.getId());
          goodsIds = new ArrayList<>();
          for(SaleOrderItem saleOrderItem: saleOrder.getItems()) {
            goodsIds.add(saleOrderItem.getGoodsId());
          }
          this.customerBusinessId = saleOrder.getDownStreamCompanyId();
          this.originalOrderId = saleOrder.getId();
          this.originalOrderCode = saleOrder.getCode();
          break;
        //查找采购单
        case 2:
          PurchaseOrderReadRepository purchaseOrderReadRepository = (PurchaseOrderReadRepository) SpringContextUtils.getBean("purchaseOrderRepositoryImpl");
          Wrapper purWrapper = Wrapper.build().append(Restrictions.eq("code",orderCode));
          PurchaseOrder purchaseOrder = purchaseOrderReadRepository.getOne(purWrapper);
          if (purchaseOrder == null) {
            throw new BusinessException("找不到单号为"+orderCode+"的采购单，请检查单号或单据类型");
          }
          purchaseOrder = purchaseOrderReadRepository.getById(purchaseOrder.getId());
          goodsIds = new ArrayList<>();
          for(PurchaseOrderItem purchaseOrderItem: purchaseOrder.getItems()) {
            goodsIds.add(purchaseOrderItem.getGoodsId());
          }
          this.customerBusinessId = purchaseOrder.getUpStreamCompanyId();
          this.originalOrderId = purchaseOrder.getId();
          this.originalOrderCode = purchaseOrder.getCode();
          break;
        //查找销售退货单
        case 3:
          SaleReturnOrderReadRepository saleReturnOrderReadRepository = (SaleReturnOrderReadRepository) SpringContextUtils.getBean(SaleReturnOrderReadRepository.class);
          Wrapper saleReturnWrapper = Wrapper.build().append(Restrictions.eq("code",orderCode));
          SaleReturnOrder saleReturnOrder = saleReturnOrderReadRepository.getOne(saleReturnWrapper);
          if (saleReturnOrder == null) {
            throw new BusinessException("找不到单号为"+orderCode+"的销售退货单，请检查单号或单据类型");
          }
          saleReturnOrder = saleReturnOrderReadRepository.getById(saleReturnOrder.getId());
          goodsIds = new ArrayList<>();
          for(SaleReturnOrderItem saleReturnOrderItem: saleReturnOrder.getItems()) {
            goodsIds.add(saleReturnOrderItem.getGoodsId());
          }
          this.customerBusinessId = saleReturnOrder.getDownStreamCompanyId();
          this.originalOrderId = saleReturnOrder.getId();
          this.originalOrderCode = saleReturnOrder.getCode();
          break;
        //查找采购退货单
        case 4:
          PurReturnOrderReadRepository purReturnOrderReadRepository = (PurReturnOrderReadRepository) SpringContextUtils.getBean(PurReturnOrderReadRepository.class);
          Wrapper purReturnWrapper = Wrapper.build().append(Restrictions.eq("returnCode",orderCode));
          PurchaseReturnOrder purchaseReturnOrder = purReturnOrderReadRepository.getOne(purReturnWrapper);
          if (purchaseReturnOrder == null) {
            throw new BusinessException("找不到单号为"+orderCode+"的采购退货单，请检查单号或单据类型");
          }
          purchaseReturnOrder = purReturnOrderReadRepository.getById(purchaseReturnOrder.getId());
          goodsIds = new ArrayList<>();
          for(PurchaseReturnOrderItem purchaseReturnOrderItem: purchaseReturnOrder.getItems()) {
            goodsIds.add(purchaseReturnOrderItem.getGoodsId());
          }
          this.customerBusinessId = purchaseReturnOrder.getUpStreamCompanyId();
          this.originalOrderId = purchaseReturnOrder.getId();
          this.originalOrderCode = purchaseReturnOrder.getReturnCode();
          break;
        default:
          throw new BusinessException("单据类型错误");
      }
    } else {
      this.lossType = null;
    }
    return goodsIds;
  }

  /**
   * 检查是否包含该商品
   * @param goodsId 商品id
   * @param goodsIds 商品id列表
   * @return
   */
  private boolean checkGoods(Long goodsId, List<Long> goodsIds) {
    for (Long id: goodsIds) {
      if (goodsId.equals(id)) {
        return true;
      }
    }
    return false;
  }

  /**
   * 删除报损单
   */
  public void destroy() {
    //判断当前状态是否能删除
    if (!StockLossEnums.Status.CANCEL.getStatus().equals(this.status)) {
      logger.error("destroy:报损单不是取消状态,不可以删除");
      throw new BusinessException("报损单不是取消状态,不可以删除");
    }
    if (StockLossEnums.HaveRecord.HAVE_RECORD.getHaveRecord().equals(this.haveRecord)) {
      throw new BusinessException("报损单已经生成出入库记录,不可以删除");
    }
  }

  /**
   * 增加报损单项
   *
   * @param item
   * @return
   */
  public StockLoss addItem(StockLossItem item) {
    this.lossItems.add(item);
    return this;
  }

  /**
   * 已确认
   */
  public void enabled() {
    this.status = StockLossEnums.Status.CONFIRM.getStatus();
    this.haveRecord = StockLossEnums.HaveRecord.HAVE_RECORD.getHaveRecord();
    this.lockStatus = StockLossEnums.LockStatus.LOCK.getLockStatus();
    this.lossDateTimestamp = System.currentTimeMillis();//确认时间
  }

  /**
   * 未确认
   */
  public void disabled() {
    this.status = StockLossEnums.Status.STAY_CONFIRM.getStatus();
    this.lossDateTimestamp = null;
  }

  /**
   * 取消
   */
  public void cancel() {
    if (!this.status.equals(StockLossEnums.Status.STAY_CONFIRM.getStatus())) {
      throw new BusinessException("报损单不是未确认状态,不可以取消");
    }
    this.status = StockLossEnums.Status.CANCEL.getStatus();
  }

  /**
   * 解锁
   */
  public void unLock() {
    if (!this.lockStatus.equals(StockLossEnums.LockStatus.LOCK.getLockStatus())) {
      throw new BusinessException("只有锁定状态才能解锁");
    }
    this.lockStatus = StockLossEnums.LockStatus.UNLOCK.getLockStatus();
  }

  /**
   * 报损单号
   */
  private String lossOrderCode;
  /**
   * 对应单号
   */
  private String originalOrderCode;
  /**
   * 对应单id
   */
  private Long originalOrderId;
  /**
   * 单据状态(0：未确认 1：已确认 -1：已取消)
   */
  private Short status;
  /**
   * 报损类型(1:销售运输报损 2:采购运输报损 3:销售退货运输报损 4:采购退货运输报损)
   */
  private Short lossType;
  /**
   * 是否已生成记录(库存记录、应收付记录等)
   */
  private Short haveRecord;
  /**
   * 仓库id
   */
  private Long storehouseId;
  /**
   * 报损时间(确认的时间)
   */
  private Long lossDateTimestamp;
  /**
   * 备注
   */
  private String remark;
  /**
   * 报损总额
   */
  private BigDecimal totalAmount;
  /**
   * 客户id
   */
  private Long customerBusinessId;
  /**
   * 录单人员id
   */
  private Long operatorId;
  /**
   * 录单日期
   */
  private Long inputDateTimestamp;
  /**
   * 锁定状态(0 未锁定 1 已锁定 )确认后自动锁定，锁定后为最终状态，将不可再修改
   */
  private Short lockStatus;
  /**
   * 报损单项
   */
  protected List<StockLossItem> lossItems;
}
